package gui.util;

import java.util.*;
import java.io.File;

import javax.swing.tree.TreeNode;


/**
 * This is an implementation of <code>TreeNode</code> that displays each
 * node as a <code>File</code> with checkbox. It will load children
 * lazily and also maintains selection hierarchy via ObserverTreestateCheckBox.
 * 
 * <code>FileTreeNode</code> makes use of <code>FileCheckBox</code> and could be
 * considered as <code>TreeNode</code> wrapper for it.
 * 
 * @author Jakub
 *
 */
public class FileTreeNode implements TreeNode {
	
	//Checkbox instance for the File represented by actual node */
	private FileCheckBox checkbox;
	//Parent node
	private FileTreeNode parent;
	//Collection of children nodes
	private List<FileTreeNode> children; 
	//Indicates whether children have already been loaded (Lazy loading)
	private boolean loaded;
	
	
	public FileTreeNode(File file, FileTreeNode parent) {
		// Wrapped FileCheckBox is being created
		this.checkbox = new FileCheckBox(file);
		// Setting parent TreeNode
		this.parent = parent;
		//Children, so far empty
		children = new ArrayList<FileTreeNode>();

//		for (File f : file.listFiles()) {
//			System.out.println(f.getName());
//		}
//		System.out.println(file.isDirectory());
//		System.out.println(file.getName());
//		System.out.println(file.getAbsolutePath());
	}
	
	/** This constructor doesn't specify a parent TreeNode. Its main purpose is
	 *  for creating a root nodes
	 * @param file File which is to be represented by this FileTreeNode in the 
	 * Tree
	 */
	public FileTreeNode(File file) {
		this(file, null);
	}
	

	/** Returns <code>Enumeration</code> of all children */
	@Override
	public Enumeration children() {
		//Children first need to be initialized
		loadChildren();
		return new FileEnumeration();
	}

	/** Indicates whether node allows children. This is usually closely tied 
	 *  with <code>File</code> being directory/file. Since we at the moment need
	 *  only directories as nodes, the method always returns true
	 */
	@Override
	public boolean getAllowsChildren() {
		return true;
	}

	/** Returns child node at specified index */
	@Override
	public TreeNode getChildAt(int index) {
		//Loading children - decision whether to actually load children or not
		//(i.e. children are already loaded) is made in loadChildren()
		loadChildren();
		return children.get(index);
	}
	
	/** Gives count of children. */
	@Override
	public int getChildCount() {
		//If the node is leaf, it has no children and therefore returns 0
		if(!isLeaf()){
			loadChildren();
			return children.size();
		}
		else return 0;
	}

	/** Returns index (position in the children collection) of the child node 
	 *  given as parameter.
	 */
	@Override
	public int getIndex(TreeNode node) {
		//zde nemusíme děti načítat, protože dokud nejsou děti načtený, v 
		// parametru stejně nemůže být nic smysluplného - nody ještě nejsou
		// vytvořené.
		return children.indexOf(node);
	}

	/** Returns parent node of receiver */ 
	@Override
	public TreeNode getParent() {
		return parent;
	}

	/** Tells if the receiver is leaf or branch. For the directory/file context
	 *  it means true for directory with subdirectories and false for file and
	 *  directory without subdirectories.
	 *  
	 *  @returns True, if the receiver is leaf, false is it's branch.
	 */
	@Override
	public boolean isLeaf() {
		File[] f = checkbox.getFile().listFiles();
		if(f == null || f.length == 0) return true;
		return false;
	}
	
	/** Returns the <code>FileCheckBox</code> represented by this node */
	public FileCheckBox getFileCheckBox() {
		return checkbox;
	}
	
	//Loads children files/directories and creates FileTreeNodes from them and
	//stores them in "children" attribute. In the end it sets "loaded" to
	//indicate that children have already been loaded
	private void loadChildren(){
		if(!loaded){
			//Loads children files (actual Files from filesystem)
			File[] files = checkbox.getFile().listFiles();
			
			//If checkbox hold real file (not directory), listFiles returns null
			if (files == null) {
				loaded = true;
				return;
			}
			for(int i = 0; i < files.length; i++){
				FileTreeNode node = new FileTreeNode(files[i], this);
				
				//We need to project selection state of parent node(this) to 
				//newly created node(children). It can be either selected or
				//unselected. It cannot be undecided, because the current parent
				//node(this) is being expanded for the first time and therefore
				//cannot have particular children selected
				node.getFileCheckBox().setState(checkbox.getState());
				
				//Setting up the slaves for parent node(directory)
				checkbox.addSlave(node.getFileCheckBox());
				children.add(node);
			}
			loaded = true;
		}
	}
	
	//Class for Enumeration of children (I don't know what the use of this is)
	//but it's required by TreeNode interface
	private class FileEnumeration implements Enumeration<FileTreeNode>
	{
		//index of the enumeration (for iterating over it)
		int currentIndex;
		
		@Override public boolean hasMoreElements(){
			return currentIndex < children.size();
		}

		@Override public FileTreeNode nextElement(){
			FileTreeNode f = children.get(currentIndex++);
			return f;
		}
	}
}
